package com.hyjiacan.tools.http.models

import com.hyjiacan.tools.http.SessionManager
import com.hyjiacan.tools.utils.Exts.endsWith
import com.hyjiacan.tools.utils.Json
import com.sun.net.httpserver.HttpExchange
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.io.InputStreamReader
import java.net.URLDecoder
import kotlin.reflect.typeOf

class RequestContext(var exchange: HttpExchange, sessionEnabled: Boolean) {
    var path: String = exchange.requestURI.path
    val method: String = exchange.requestMethod
    val contentType: String? = exchange.requestHeaders["Content-Type"]?.firstOrNull()
    val params: Map<String, String>
    val body: Any
    var form: Map<String, Any>
    val cookies: Map<String, String>
    val session: Session?

    companion object {
        const val GET = "GET"
        const val POST = "POST"
        const val PUT = "PUT"
        const val DELETE = "DELETE"
    }

    init {
        params = getQueryParams()
        body = getBodyData()
        form = getFormData()
        cookies = getCookiesFromRequest()
        session = if (sessionEnabled) {
            getSessionByCookie()
        } else {
            null
        }

        println("$method $path")
    }

    private fun getQueryParams(): Map<String, String> {
        val query = exchange.requestURI.query
        val params = mutableMapOf<String, String>()

        if (query == null) {
            return params
        }

        query.split('&').forEach {
            val idx = it.indexOf('=')
            if (idx == -1) {
                params[it] = ""
            } else {
                val name = it.substring(0, idx)
                val value = it.substring(idx + 1)
                params[name] = value
            }
        }

        return params
    }

    private fun getBodyData(): Any {
        if (method == "GET" || method == "DELETE") {
            return emptyMap<String, String>()
        }
        val contentType = this.contentType ?: "application/octet-stream"

        return when {
            contentType.contains("application/json") -> {
                val streamReader = InputStreamReader(exchange.requestBody)
                val body = streamReader.readText()
                streamReader.close()
                if (body.isEmpty()) {
                    return emptyMap<String, String>()
                }
                val obj = Json.parse(body)
                obj.mapValues { it.value?.toString() ?: "" }
            }

            contentType.contains("application/x-www-form-urlencoded") -> {
                val streamReader = InputStreamReader(exchange.requestBody)
                val body = streamReader.readText()
                streamReader.close()
                if (body.isEmpty()) {
                    return emptyMap<String, String>()
                }
                body.split("&").associate {
                    val (key, value) = it.split("=")
                    key to URLDecoder.decode(value, "UTF-8")
                }
            }

            contentType.contains("multipart/form-data") -> {
                // 处理 multipart/form-data
                parseMultipartFormData(exchange.requestBody, contentType)
            }

            contentType.contains("application/octet-stream") -> {
                // 处理二进制数据
                exchange.requestBody.readBytes() // 返回字节数组
            }

            else -> {
                // 处理其他类型或返回默认值
                "Unsupported Content-Type $contentType"
            }
        }
    }

    @OptIn(kotlin.ExperimentalStdlibApi::class)
    private fun parseMultipartFormData(inputStream: InputStream, contentType: String): Map<String, Any> {
        val boundary = contentType.split("boundary=")[1].trim()
        val boundaryBytes = "--$boundary".toByteArray(Charsets.UTF_8)
        val formData = mutableMapOf<String, Any>()

        val buffer = ByteArray(1024)
        val outputStream = ByteArrayOutputStream()
        var currentFieldName: String?
        var currentFileName: String?

        while (true) {
            val bytesRead = inputStream.read(buffer)
            if (bytesRead == -1) break // 读取结束

            for (i in 0 until bytesRead) {
                outputStream.write(buffer[i].toInt())

                // 检查是否到达边界
                if (outputStream.size() >= boundaryBytes.size && outputStream.toByteArray().endsWith(boundaryBytes)) {
                    // 处理当前字段
                    val partData = outputStream.toByteArray().dropLast(boundaryBytes.size + 4) // 去掉边界和结束符
                    val headers = String(partData.takeWhile { it != '\r'.code.toByte() }.toByteArray(), Charsets.UTF_8)
                    val body = partData.dropWhile { it != '\r'.code.toByte() }.drop(4) // 跳过 \r\n\r\n

                    val contentDisposition = headers.lines().firstOrNull { it.startsWith("Content-Disposition") }
                    if (contentDisposition != null) {
                        currentFieldName = Regex("name=\"([^\"]+)\"").find(contentDisposition)?.groups?.get(1)?.value
                        currentFileName = Regex("filename=\"([^\"]+)\"").find(contentDisposition)?.groups?.get(1)?.value

                        if (currentFileName != null) {
                            // 处理文件上传
                            formData[currentFieldName!!] = FileData(currentFileName, body.toByteArray())
                        } else if (currentFieldName != null) {
                            // 处理普通表单字段
                            formData[currentFieldName] = String(body.toByteArray(), Charsets.UTF_8)
                        }
                    }

                    // 重置输出流以准备下一个部分
                    outputStream.reset()
                }
            }
        }

        return formData
    }


    private fun getFormData(): Map<String, Any> {
        try {
            @Suppress("UNCHECKED_CAST")
            return body as HashMap<String, Any>
        } catch (e: Exception) {
            return emptyMap()
        }
    }

    private fun getCookiesFromRequest(): Map<String, String> {
        val cookieString = exchange.requestHeaders.getFirst("cookie") ?: return emptyMap()
        val cookies = HashMap<String, String>()
        for (cookie in cookieString.split(";")) {
            val (key, value) = cookie.split("=")
            cookies[key.trim()] = value.trim()
        }
        return cookies
    }

    private fun getSessionByCookie(): Session {
        if (!cookies.containsKey(SessionManager.COOKIE_NAME)) {
            return SessionManager.make()
        }
        val sessionId = cookies[SessionManager.COOKIE_NAME]!!
        return SessionManager.get(sessionId) ?: return SessionManager.make()
    }

    data class FileData(val fileName: String, val content: ByteArray)
}